package com.mjf.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mjf.cjt.domain.TmAccountDicEntity;
import com.mjf.cjt.domain.TmDepartmentDicEntity;
import com.mjf.cjt.domain.VoucherEntity;
import com.mjf.cjt.service.TmAccountDicService;
import com.mjf.cjt.service.TmDepartmentDicService;
import com.mjf.cjt.service.VoucherService;
import com.mjf.cjt.util.TMUtils;
import com.mjf.system.domain.BalanceRecord;
import com.mjf.system.service.vo.*;
import com.mjf.system.domain.CashCounterpartyAccount;
import com.mjf.system.mapper.BalanceRecordMapper;
import com.mjf.system.service.IBalanceRecordService;
import com.mjf.system.service.ICashCounterpartyAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * desc
 * </p>
 *
 * @author chenshoumeng
 * @date 2025/4/13 17:59
 */
@Service
public class BalanceRecordServiceImpl extends ServiceImpl<BalanceRecordMapper, BalanceRecord> implements IBalanceRecordService {


    @Autowired
    private BalanceRecordMapper balanceRecordMapper;
    @Autowired
    private VoucherService voucherService;
    @Autowired
    private TmDepartmentDicService tmDepartmentDicService;
    @Autowired
    private TmAccountDicService tmAccountDicService;
    @Autowired
    private ICashCounterpartyAccountService cashCounterpartyAccountService;

    public static final String JUR_LOCK = "";
    @Override
    public IPage<BalanceRecord> getBalanceRecordList(Integer page, Integer size, String accountingperiod) {
        LambdaQueryWrapper<BalanceRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StrUtil.isNotBlank(accountingperiod), BalanceRecord::getAccountingperiod,
            accountingperiod);
        Page<BalanceRecord> entityPage = new Page<>(page, size);
        return balanceRecordMapper.selectPage(entityPage, queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void jur(BalanceJurVO balanceJurVO) {
        if (StrUtil.isBlank(balanceJurVO.getAccountingperiod())) {
            throw new RuntimeException("请选择会计期间");
        }
        synchronized (JUR_LOCK) {
            List<BalanceRecord> insertList = new ArrayList<>();

            // 查询科目
            List<TmAccountDicEntity> accountDicEntities = tmAccountDicService.list();
            Map<Integer, TmAccountDicEntity> accMap = accountDicEntities.stream().collect(Collectors.toMap(TmAccountDicEntity::getId,
                Function.identity()));

            // 查询部门
            List<TmDepartmentDicEntity> departmentDicEntities = tmDepartmentDicService.list();
            Map<Integer, TmDepartmentDicEntity> depMap = departmentDicEntities.stream().collect(Collectors.toMap(TmDepartmentDicEntity::getId,
                Function.identity()));

            // 查询会计期间的凭证
            List<VoucherEntity> voucherEntities = voucherService.list(Wrappers.<VoucherEntity>lambdaQuery()
                .likeRight(VoucherEntity::getVoucherdate, balanceJurVO.getAccountingperiod()));

            // 查询期初余额数据
            List<BalanceRecord> balanceRecordEntities = list(Wrappers.<BalanceRecord>lambdaQuery()
                .eq(BalanceRecord::getAccountingperiod, TMUtils.getBeginAccountingPeriod(balanceJurVO.getAccountingperiod()))
            );
//            //  按科目+部门分组
            Map<String, List<BalanceRecord>> balMap =
                balanceRecordEntities.stream().collect(Collectors.groupingBy(e -> e.getIdaccount() + "_" +
                   e.getIdauxaccdepartment()));
            Map<String, BalanceRecord> balMapSum = new HashMap<>();

//            // 找到距离计算期间最近的一次余额记录, 取出期末余额, 作为本次的期初余额
            for (Map.Entry<String, List<BalanceRecord>> entry : balMap.entrySet()) {
                balMapSum.put(entry.getKey(), entry.getValue().get(0));
            }

            if (CollectionUtil.isNotEmpty(voucherEntities)) {
                Map<String, List<VoucherEntity>> vouMap =
                    voucherEntities.stream().collect(Collectors.groupingBy(e -> e.getIdaccount() + "_" + e.getIdauxaccdepartment() ));
                vouMap.forEach((k, v) -> {
                    BalanceRecord balanceRecordEntity = new BalanceRecord();
                    balanceRecordEntity.setAccountingperiod(balanceJurVO.getAccountingperiod());
                    balanceRecordEntity.setAccountingyear(balanceJurVO.getAccountingperiod().substring(0, 4));
                    // 科目
                    balanceRecordEntity.setIdaccount(v.get(0).getIdaccount());
                    balanceRecordEntity.setIdaccountName(Optional.ofNullable(accMap.get(v.get(0).getIdaccount()))
                        .map(TmAccountDicEntity::getName).orElse(""));
                    balanceRecordEntity.setIdaccountCode(Optional.ofNullable(accMap.get(v.get(0).getIdaccount()))
                        .map(TmAccountDicEntity::getCode).orElse(""));
                    // 部门
                    balanceRecordEntity.setIdauxaccdepartment(v.get(0).getIdauxaccdepartment());
                    balanceRecordEntity.setIdauxaccdepartmentName(Optional.ofNullable(depMap.get(v.get(0).getIdauxaccdepartment()))
                        .map(TmDepartmentDicEntity::getName).orElse(""));
                    balanceRecordEntity.setIdauxaccdepartmentCode(Optional.ofNullable(depMap.get(v.get(0).getIdauxaccdepartment()))
                        .map(TmDepartmentDicEntity::getCode).orElse(""));
                    // 期初余额
                    balanceRecordEntity.setBeginBalance(Optional.ofNullable(balMapSum.get(k)).map(BalanceRecord::getEndingBalance).orElse(BigDecimal.ZERO));
                    // 借方累计
                    balanceRecordEntity.setOrigamountdrsum(v.stream().map(VoucherEntity::getOrigamountdr)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add));
                    // 贷方累计
                    balanceRecordEntity.setOrigamountcrsum(v.stream().map(VoucherEntity::getOrigamountcr)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add));
                    balanceRecordEntity.setFdc(Optional.ofNullable(accMap.get(v.get(0).getIdaccount())).map(TmAccountDicEntity::getDcdirectionCode).orElse(""));
                    if ("00".equals(Optional.ofNullable(accMap.get(v.get(0).getIdaccount())).map(TmAccountDicEntity::getDcdirectionCode).orElse(""))) {
                        // 会计科目中余额方向是借方，期初余额+借方发生额-贷方发生额=期末余额
                        balanceRecordEntity.setEndingBalance(balanceRecordEntity.getBeginBalance()
                            .add(balanceRecordEntity.getOrigamountdrsum())
                            .subtract(balanceRecordEntity.getOrigamountcrsum()));
                    }
                    if ("01".equals(Optional.ofNullable(accMap.get(v.get(0).getIdaccount())).map(TmAccountDicEntity::getDcdirectionCode).orElse(""))) {
                        // 会计科目中余额方向是贷方，期初余额+贷方发生额-借方发生额=期末余额
                        balanceRecordEntity.setEndingBalance(balanceRecordEntity.getBeginBalance()
                            .add(balanceRecordEntity.getOrigamountcrsum())
                            .subtract(balanceRecordEntity.getOrigamountdrsum()));
                    }
                    insertList.add(balanceRecordEntity);
                });
                // 过滤当前期间的数据
                List<String> igKeys = new ArrayList<>();
                List<String> keys =
                    insertList.stream().map(e -> e.getIdaccount() + "_" + e.getIdauxaccdepartment()).collect(Collectors.toList());
                balMapSum.forEach((k, v) -> {
                    if (!keys.contains(k)) {
                        igKeys.add(k);
                    }
                });

                // 当前期间未发生的, 上一个期间存在的科目，取上期的期末余额作为期初余额, 期末余额
                if (CollectionUtil.isNotEmpty(balMapSum.keySet())) {
                    igKeys.forEach(k -> {
                        BalanceRecord balanceRecordEntity = new BalanceRecord();
                        BeanUtil.copyProperties(balMapSum.get(k), balanceRecordEntity);
                        balanceRecordEntity.setId(null);
                        balanceRecordEntity.setOrigamountdrsum(BigDecimal.ZERO);
                        balanceRecordEntity.setOrigamountcrsum(BigDecimal.ZERO);
                        balanceRecordEntity.setAccountingperiod(balanceJurVO.getAccountingperiod());
                        balanceRecordEntity.setAccountingyear(balanceJurVO.getAccountingperiod().substring(0, 4));
                        balanceRecordEntity.setBeginBalance(balanceRecordEntity.getEndingBalance());
                        insertList.add(balanceRecordEntity);
                    });
                }
                // 清除当前区间数据
                remove(Wrappers.<BalanceRecord>lambdaQuery()
                    .eq(BalanceRecord::getAccountingperiod, balanceJurVO.getAccountingperiod()));
                saveBatch(insertList);
            }
        }
    }


    @Override
    public List<CblrfxVO> getCblrfxList(List<String> accountingperiods) {

        //
        String start  = accountingperiods.get(0);
        String end  = accountingperiods.get(accountingperiods.size() - 1);
        String depCode = "";
        // 查询当前期间余额记录
        List<BalanceRecord> balanceRecordEntities1 = list(Wrappers.<BalanceRecord>lambdaQuery()
            .ge(BalanceRecord::getAccountingperiod, accountingperiods.get(0))
            .le(BalanceRecord::getAccountingperiod, accountingperiods.get(1))
            .eq(StrUtil.isNotBlank(depCode), BalanceRecord::getIdauxaccdepartmentCode, depCode)
            .orderByAsc(BalanceRecord::getAccountingperiod)
        );

        String accountingperiod = accountingperiods.get(accountingperiods.size() - 1);
        // 查询累计余额记录(小于等于查询截止期间)
        List<BalanceRecord> ljBalRecordEntities = list(Wrappers.<BalanceRecord>lambdaQuery()
            .le(BalanceRecord::getAccountingperiod, accountingperiod)
            .eq(StrUtil.isNotBlank(depCode), BalanceRecord::getIdauxaccdepartmentCode, depCode)
            .orderByDesc(BalanceRecord::getAccountingperiod)
        );
        // 累计余额记录按部门分组
        Map<String, List<BalanceRecord>> ljBalMap =
            ljBalRecordEntities.stream().collect(Collectors.groupingBy(BalanceRecord::getIdauxaccdepartmentCode));

        //  按科目+部门分组
        Map<String, List<BalanceRecord>> balMap =
            ljBalRecordEntities.stream().collect(Collectors.groupingBy(e -> e.getIdaccount() + "_" +
                e.getIdauxaccdepartment()));

        // 取查询截止期间的期初,期末金额
        List<BalanceRecord> qcmList = new ArrayList<>();
        for (Map.Entry<String, List<BalanceRecord>> entry : balMap.entrySet()) {
            List<BalanceRecord> qc =
                entry.getValue().stream().filter(e -> !e.getAccountingperiod().equals(end)).collect(Collectors.toList());
            List<BalanceRecord> qm =
                entry.getValue().stream().filter(e -> e.getAccountingperiod().equals(end)).collect(Collectors.toList());
            BalanceRecord qcBal = new BalanceRecord();
            BeanUtil.copyProperties(entry.getValue().get(0), qcBal);
            // 取到查询截止期间的数据直接取期初,期末
            if (CollectionUtil.isNotEmpty(qm)) {
                qcBal.setBeginBalance(qm.get(0).getBeginBalance());
                qcBal.setEndingBalance(qm.get(0).getEndingBalance());
            } else {
                // 未取到截止期间的数据, 取到查询期间最近的一次余额记录
                if (CollectionUtil.isNotEmpty(qc)) {
                    qcBal.setEndingBalance(qc.get(0).getEndingBalance());
                    qcBal.setBeginBalance(qc.get(0).getEndingBalance());
                } else {
                    // 未取到查询期间最近的一次余额记录, 设置为0
                    qcBal.setBeginBalance(BigDecimal.ZERO);
                    qcBal.setEndingBalance(BigDecimal.ZERO);
                }
            }
            qcmList.add(qcBal);
        }
//        // 按部门分组期初,末余额
//        Map<String, List<BalanceRecord>> qcmMap =
//            qcmList.stream().collect(Collectors.groupingBy(BalanceRecord::getIdauxaccdepartmentCode));
//
//

        // 按部门分组
        Map<String, List<BalanceRecord>> accBalMap =
            balanceRecordEntities1.stream().collect(Collectors.groupingBy(BalanceRecord::getIdauxaccdepartmentCode));

        // 查询部门
        List<TmDepartmentDicEntity> departmentDicEntities =
            tmDepartmentDicService.list(Wrappers.<TmDepartmentDicEntity>lambdaQuery().orderByAsc(TmDepartmentDicEntity::getCode));
        departmentDicEntities =
            departmentDicEntities.stream().filter(e -> StrUtil.isBlank(depCode) || e.getCode().equals(depCode)).collect(Collectors.toList());
        Map<String, TmDepartmentDicEntity> depMap =
            departmentDicEntities.stream().collect(Collectors.toMap(TmDepartmentDicEntity::getCode,
            Function.identity()));

        // 查询第一次期初
        List<BalanceRecord> balanceRecordEntities2 = list(Wrappers.<BalanceRecord>lambdaQuery()
            .eq(BalanceRecord::getAccountingperiod, "2022-10"));

        // 按部门分组
        Map<String, List<BalanceRecord>> balMapFirst =
            balanceRecordEntities2.stream().collect(Collectors.groupingBy(BalanceRecord::getIdauxaccdepartmentCode));

        // 查询科目
        List<TmAccountDicEntity> accountDicEntities = tmAccountDicService.list();
        Map<Integer, TmAccountDicEntity> accMap = accountDicEntities.stream().collect(Collectors.toMap(TmAccountDicEntity::getId,
            Function.identity()));
        CblrfxVO voTotal = CblrfxVO.builder()
            .sr(BigDecimal.ZERO)
            .cb(BigDecimal.ZERO)
            .cashBegin(BigDecimal.ZERO)
            .cashDr(BigDecimal.ZERO)
            .cashCr(BigDecimal.ZERO)
            .cashEnd(BigDecimal.ZERO)
            .ljcashBegin(BigDecimal.ZERO)
            .ljcashDr(BigDecimal.ZERO)
            .ljcashCr(BigDecimal.ZERO)
            .ljsr(BigDecimal.ZERO)
            .ljcb(BigDecimal.ZERO)
            .lr(BigDecimal.ZERO)
            .zjje(BigDecimal.ZERO)
            .yfzk(BigDecimal.ZERO)
            .yszk(BigDecimal.ZERO)
            .ytcb(BigDecimal.ZERO)
            .ytcb2(BigDecimal.ZERO)
            .cbgj(BigDecimal.ZERO)
            .zlr(BigDecimal.ZERO)
            .remark(BigDecimal.ZERO)
            .departParentName("合计")
            .build();
        List<CblrfxVO> vos = new ArrayList<>();
        for (Map.Entry<String, TmDepartmentDicEntity> entry : depMap.entrySet()) {
            // 当前部门+期间余额记录
            List<BalanceRecord> list = Optional.ofNullable(accBalMap.get(entry.getKey())).orElse(new ArrayList<>());
            // 累计期初
            List<BalanceRecord> listFirst = Optional.ofNullable(balMapFirst.get(entry.getKey())).orElse(new ArrayList<>());
            // 累计所有
            List<BalanceRecord> listLj = Optional.ofNullable(ljBalMap.get(entry.getKey())).orElse(new ArrayList<>());
//
//            // 当前部门期初,末
//            List<BalanceRecord> qcmList1 =
//                Optional.ofNullable(qcmMap.get(entry.getKey())).orElse(new ArrayList<>());

            /**
             * 现金	1
             * 收入	2
             * 成本	3
             * 费用	4
             * 应收	5
             * 应付	6
             */
            CblrfxVO vo = CblrfxVO.builder()
                // 收入 accounttypedto_code = 2  贷方发生额
                .sr(list.stream().filter(e -> "2".equals(accMap.get(e.getIdaccount()).getAccType()))
                    .map(BalanceRecord::getOrigamountcrsum)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 成本  accounttypedto_code = 3 借方发生额 + 7 期末余额 (借方) - 7 期末余额 (贷方)
                .cb(list.stream().filter(e ->
                        "3".equals(accMap.get(e.getIdaccount()).getAccType()))
                    .map(BalanceRecord::getOrigamountdrsum)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                )
                // 现金科目期初 accounttypedto_code = 1 查询起始期间的期初余额
                .cashBegin(list.stream().filter(e ->
                        "1".equals(accMap.get(e.getIdaccount()).getAccType())
                            && start.equals(e.getAccountingperiod())
                    )
                    .map(BalanceRecord::getBeginBalance)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 现金科目借方发生 accounttypedto_code = 1 借方发生额
                .cashDr(list.stream().filter(e -> "1".equals(accMap.get(e.getIdaccount()).getAccType()))
                    .map(BalanceRecord::getOrigamountdrsum)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 现金科目贷方发生 accounttypedto_code = 1 贷方发生额
                .cashCr(list.stream().filter(e -> "1".equals(accMap.get(e.getIdaccount()).getAccType()))
                    .map(BalanceRecord::getOrigamountcrsum)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 现金科目期末余额 accounttypedto_code = 1 查询结束期间期末余额
                .cashEnd(list.stream().filter(e ->
                        "1".equals(accMap.get(e.getIdaccount()).getAccType())
                            && end.equals(e.getAccountingperiod())
                    )
                    .map(BalanceRecord::getEndingBalance)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 累计现金科目期初 accounttypedto_code = 1 建账时 期初余额
                .ljcashBegin(listFirst.stream().filter(e -> "1".equals(accMap.get(e.getIdaccount()).getAccType()))
                    .map(BalanceRecord::getEndingBalance)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 累计现金科目借方发生 accounttypedto_code = 1 借方发生额
                .ljcashDr(listLj.stream().filter(e -> "1".equals(accMap.get(e.getIdaccount()).getAccType()))
                    .map(BalanceRecord::getOrigamountdrsum)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 累计现金科目贷方发生 建账开始 accounttypedto_code = 1 贷方发生额
                .ljcashCr(listLj.stream().filter(e -> "1".equals(accMap.get(e.getIdaccount()).getAccType()))
                    .map(BalanceRecord::getOrigamountcrsum)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 收入累计 建账开始  accounttypedto_code = 2 期末余额
                .ljsr(listLj.stream().filter(e ->
                        "2".equals(accMap.get(e.getIdaccount()).getAccType())
                            && end.equals(e.getAccountingperiod())
                    )
                    .map(BalanceRecord::getEndingBalance)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 成本累计 建账开始  accounttypedto_code = 3 期末余额
                .ljcb(listLj.stream().filter(e ->
                        "3".equals(accMap.get(e.getIdaccount()).getAccType())
                         && end.equals(e.getAccountingperiod())
                    )
                    .map(BalanceRecord::getEndingBalance)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                    .add(list.stream().filter(e ->
                            "7".equals(accMap.get(e.getIdaccount()).getAccType())
                                && "00".equals(e.getFdc()) // 借方
                                && end.equals(e.getAccountingperiod()) // 查询截止期间的期末余额
                        )
                        .map(BalanceRecord::getEndingBalance)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add))
                    .subtract(list.stream().filter(e ->
                            "7".equals(accMap.get(e.getIdaccount()).getAccType())
                                && "01".equals(e.getFdc()) // 贷方
                                && end.equals(e.getAccountingperiod()) // 查询截止期间的期末余额
                        )
                        .map(BalanceRecord::getEndingBalance)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add))
                )
                // 资金结余 现金科目 accounttypedto_code = 1 期末余额
                .zjje(listLj.stream().filter(e ->
                        "1".equals(accMap.get(e.getIdaccount()).getAccType())
                            && end.equals(e.getAccountingperiod())
                    )
                    .map(BalanceRecord::getEndingBalance)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 应付账款 应付科目 accounttypedto_code = 6 期末余额
                .yfzk(listLj.stream().filter(e ->
                        "6".equals(accMap.get(e.getIdaccount()).getAccType())
                            && end.equals(e.getAccountingperiod())
                    )
                    .map(BalanceRecord::getEndingBalance)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 应收账款 应收科目 accounttypedto_code = 5 期末余额
                .yszk(listLj.stream().filter(e ->
                        "5".equals(accMap.get(e.getIdaccount()).getAccType())
                            && end.equals(e.getAccountingperiod())
                    )
                    .map(BalanceRecord::getEndingBalance)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                // 应摊销成本
                .ytcb(BigDecimal.ZERO)
                // 应摊销成本2
                .ytcb2(BigDecimal.ZERO)
                .build();;
            // 利润 = 收入累计 - 成本累计
            vo.setLr(vo.getLjsr().subtract(vo.getLjcb()));
            // 利润率
            if (BigDecimal.ZERO.compareTo(vo.getLjsr()) != 0) {
                BigDecimal value = vo.getLr().divide(vo.getLjsr(), 4, RoundingMode.HALF_UP);
                BigDecimal percent = value.multiply(BigDecimal.valueOf(100));
                String result = percent.setScale(2, RoundingMode.HALF_UP).toPlainString() + "%";
                vo.setLrl(result);
            } else {
                vo.setLrl("0.00%");
            }
            // 成本归集 成本累计 + 应摊销成本
            vo.setCbgj(vo.getLjcb().add(vo.getYtcb()));
            // 总利润 收入累计 - 应摊销成本
            vo.setZlr(vo.getLjsr().subtract(vo.getYtcb()));
            // 总利润率
            if (BigDecimal.ZERO.compareTo(vo.getLjsr()) != 0) {
                BigDecimal value = vo.getZlr().divide(vo.getLjsr(), 4, RoundingMode.HALF_UP);
                BigDecimal percent = value.multiply(BigDecimal.valueOf(100));
                String result = percent.setScale(2, RoundingMode.HALF_UP).toPlainString() + "%";
                vo.setZlrl(result);
            } else {
                vo.setZlrl("0.00%");
            }
            // 校验公式：资金结余-利润-应付账款+应收账款
            vo.setRemark(vo.getZjje().subtract(vo.getLr()).subtract(vo.getYfzk()).add(vo.getYszk()));

            TmDepartmentDicEntity dep = depMap.get(entry.getKey());
            vo.setDepartCode(entry.getValue().getCode());
            vo.setDepartName(entry.getValue().getName());
            vo.setDepartParentCode(entry.getValue().getCode());
            vo.setDepartParentName(entry.getValue().getName());
            // 查一级部门
            if (ObjectUtil.isNotNull(entry.getValue().getParentcode())) {
                vo.setDepartParentCode(entry.getValue().getParentcode());
                vo.setDepartParentName(entry.getValue().getParentname());
            }

            // 合计
            voTotal.setSr(voTotal.getSr().add(vo.getSr()));
            voTotal.setCb(voTotal.getCb().add(vo.getCb()));
            voTotal.setCashBegin(voTotal.getCashBegin().add(vo.getCashBegin()));
            voTotal.setCashDr(voTotal.getCashDr().add(vo.getCashDr()));
            voTotal.setCashCr(voTotal.getCashCr().add(vo.getCashCr()));
            voTotal.setCashEnd(voTotal.getCashEnd().add(vo.getCashEnd()));
            voTotal.setLjcashBegin(voTotal.getLjcashBegin().add(vo.getLjcashBegin()));
            voTotal.setLjcashDr(voTotal.getLjcashDr().add(vo.getLjcashDr()));
            voTotal.setLjcashCr(voTotal.getLjcashCr().add(vo.getLjcashCr()));
            voTotal.setLjsr(voTotal.getLjsr().add(vo.getLjsr()));
            voTotal.setLjcb(voTotal.getLjcb().add(vo.getLjcb()));
            voTotal.setLr(voTotal.getLr().add(vo.getLr()));
            voTotal.setZjje(voTotal.getZjje().add(vo.getZjje()));
            voTotal.setYfzk(voTotal.getYfzk().add(vo.getYfzk()));
            voTotal.setYszk(voTotal.getYszk().add(vo.getYszk()));
            voTotal.setYtcb(voTotal.getYtcb().add(vo.getYtcb()));
            voTotal.setYtcb2(voTotal.getYtcb2().add(vo.getYtcb2()));
            voTotal.setCbgj(voTotal.getCbgj().add(vo.getCbgj()));
            voTotal.setZlr(voTotal.getZlr().add(vo.getZlr()));
            voTotal.setRemark(voTotal.getRemark().add(vo.getRemark()));
            vos.add(vo);
        }
        // 利润率
        if (BigDecimal.ZERO.compareTo(voTotal.getLjsr()) != 0) {
            BigDecimal value = voTotal.getLr().divide(voTotal.getLjsr(), 4, RoundingMode.HALF_UP);
            BigDecimal percent = value.multiply(BigDecimal.valueOf(100));
            String result = percent.setScale(2, RoundingMode.HALF_UP).toPlainString() + "%";
            voTotal.setLrl(result);
        } else {
            voTotal.setLrl("0.00%");
        }
        // 成本归集 成本累计 + 应摊销成本
        voTotal.setCbgj(voTotal.getLjcb().add(voTotal.getYtcb()));
        // 总利润 收入累计 - 应摊销成本
        voTotal.setZlr(voTotal.getLjsr().subtract(voTotal.getYtcb()));
        // 总利润率
        if (BigDecimal.ZERO.compareTo(voTotal.getLjsr()) != 0) {
            BigDecimal value = voTotal.getZlr().divide(voTotal.getLjsr(), 4, RoundingMode.HALF_UP);
            BigDecimal percent = value.multiply(BigDecimal.valueOf(100));
            String result = percent.setScale(2, RoundingMode.HALF_UP).toPlainString() + "%";
            voTotal.setZlrl(result);
        } else {
            voTotal.setZlrl("0.00%");
        }
        // 按部门编码排序
        vos.sort(Comparator.comparing(CblrfxVO::getDepartCode));
        vos.add(voTotal);
        return vos;
    }

    @Override
    public Map<String, Object> getXmsrmxList(XmsrmxReqVO vo) {
        // 1001伟达现金+1002现金期初
        // 取现金科目ID
        List<Integer> ids = tmAccountDicService.list(new LambdaQueryWrapper<TmAccountDicEntity>()
            .in(TmAccountDicEntity::getCode, new ArrayList<>(Arrays.asList("1001", "1002"))))
            .stream().map(TmAccountDicEntity::getId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(ids)) {
            return Collections.EMPTY_MAP;
        }
        // 筛选现金科目的凭证where sql
        String insql = " select id from tm_account_dic where code in ('1001', '1002') ";
        if (CollectionUtil.isNotEmpty(vo.getIds())) {
            insql = vo.getIds().stream().map(String::valueOf).collect(Collectors.joining(","));
        }
        String start = vo.getAccountingperiods().get(0) + "-01 00:00:00";
        String end = vo.getAccountingperiods().get(1) + "-31 23:59:59";
        // 查询凭证 取现金科目的凭证, 非现金科目的凭证明细
        LambdaQueryWrapper<VoucherEntity> qw = new LambdaQueryWrapper<>();
        qw.ge(CollectionUtil.isNotEmpty(vo.getAccountingperiods()), VoucherEntity::getVoucherdate, start);
        qw.le(CollectionUtil.isNotEmpty(vo.getAccountingperiods()), VoucherEntity::getVoucherdate, end);
        qw.notIn(VoucherEntity::getIdaccount, ids);
        qw.inSql(VoucherEntity::getCode, "select DISTINCT code from qddl_voucher where idaccount in ( " +
            insql +
            ") ");
        qw.orderByDesc(VoucherEntity::getVoucherdate);
        List<VoucherEntity> vouchers = voucherService.list(qw);
        if (CollectionUtil.isEmpty(vouchers)) {
            return Collections.EMPTY_MAP;
        }
        // 查询凭证的现金科目
        LambdaQueryWrapper<VoucherEntity> qw1 = new LambdaQueryWrapper<>();
        qw1.ge(CollectionUtil.isNotEmpty(vo.getAccountingperiods()), VoucherEntity::getVoucherdate,
            vo.getAccountingperiods().get(0));
        qw1.le(CollectionUtil.isNotEmpty(vo.getAccountingperiods()), VoucherEntity::getVoucherdate,
            vo.getAccountingperiods().get(1));
        qw1.in(VoucherEntity::getIdaccount, ids);
        qw1.inSql(VoucherEntity::getCode, "select DISTINCT code from qddl_voucher where idaccount in ( " +
            insql +
            ") ");
        List<VoucherEntity> vouchers1 = voucherService.list(qw1);

        // 分组凭证
        Map<String, List<VoucherEntity>> map1 =
            vouchers1.stream().collect(Collectors.groupingBy(VoucherEntity::getCode));

        // 按部门+科目汇总 + 会计期间
        Map<String, List<VoucherEntity>> map =
            vouchers.stream().collect(Collectors.groupingBy(v -> v.getIdauxaccdepartment() + "_" + v.getIdaccount() + "_" + v.getVoucherdate().substring(0, 7)));

        // 查询部门
        List<TmDepartmentDicEntity> departmentDicEntities =
            tmDepartmentDicService.list(Wrappers.<TmDepartmentDicEntity>lambdaQuery().orderByAsc(TmDepartmentDicEntity::getCode));
        Map<Integer, TmDepartmentDicEntity> depMap =
            departmentDicEntities.stream().collect(Collectors.toMap(TmDepartmentDicEntity::getId,
                Function.identity()));
        // 查询科目
        List<TmAccountDicEntity> accountDicEntities = tmAccountDicService.list();
        Map<Integer, TmAccountDicEntity> accMap = accountDicEntities.stream().collect(Collectors.toMap(TmAccountDicEntity::getId,
            Function.identity()));

        List<XmsrmxVO> vos = new ArrayList<>();
        vouchers.forEach(v -> {
            // 部门
            TmDepartmentDicEntity department = depMap.get(v.getIdauxaccdepartment());
            XmsrmxVO voTmp = XmsrmxVO.builder()
                .idaccountParent(v.getIdaccount())
                .idaccountParentCode(accMap.get(v.getIdaccount()).getCode())
                .idaccountParentName(accMap.get(v.getIdaccount()).getName())
                .idaccount(v.getIdaccount())
                .idaccountCode(accMap.get(v.getIdaccount()).getCode())
                .idaccountName(accMap.get(v.getIdaccount()).getName())
                .accountingperiod(v.getVoucherdate())
                .code(v.getCode())
                .departCode(department.getCode())
                .departName(department.getName())
                .departParentCode(department.getCode())
                .departParentName(department.getName())
                .accountingperiod(v.getVoucherdate())
                .origamountcr(v.getOrigamountdr())
                .origamountdr(v.getOrigamountcr())
                .build();
            // 一级部门
            if (ObjectUtil.isNotNull(department.getParentcode())) {
                voTmp.setDepartParentCode(department.getParentcode());
                voTmp.setDepartParentName(department.getParentname());
            }
            // 一级科目
            if (ObjectUtil.isNotNull(accMap.get(v.getIdaccount()).getIdparent())) {
                voTmp.setIdaccountParent(accMap.get(accMap.get(v.getIdaccount()).getIdparent()).getIdparent());
                voTmp.setIdaccountParentCode(accMap.get(accMap.get(v.getIdaccount()).getIdparent()).getCode());
                voTmp.setIdaccountParentName(accMap.get(accMap.get(v.getIdaccount()).getIdparent()).getName());
            }
            vos.add(voTmp);
        });

        vos.sort(Comparator.comparing(XmsrmxVO::getAccountingperiod)
             .thenComparing(XmsrmxVO::getCode, Comparator.reverseOrder())
            .thenComparing(XmsrmxVO::getDepartCode, Comparator.reverseOrder()));
        return new HashMap<String, Object>() {{
            put("list",
                vos.stream().skip((long) (vo.getPage() - 1) * vo.getSize()).limit(vo.getSize()).collect(Collectors.toList()));
            put("total", vos.size());
        }};
    }

    @Override
    public Map<String, Object> getXmsrhzList(XmsrmxReqVO vo) {
        // 1001伟达现金+1002现金期初
        // 取现金科目ID
        List<Integer> ids = tmAccountDicService.list(new LambdaQueryWrapper<TmAccountDicEntity>()
                .in(TmAccountDicEntity::getCode, new ArrayList<>(Arrays.asList("1001", "1002"))))
            .stream().map(TmAccountDicEntity::getId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(ids)) {
            return Collections.EMPTY_MAP;
        }
        // 筛选现金科目的凭证where sql
        String insql = " select id from tm_account_dic where code in ('1001', '1002') ";
        if (CollectionUtil.isNotEmpty(vo.getIds())) {
            insql = vo.getIds().stream().map(String::valueOf).collect(Collectors.joining(","));
        }
        String start = vo.getAccountingperiods().get(0) + "-01 00:00:00";
        String end = vo.getAccountingperiods().get(1) + "-31 23:59:59";
        // 查询凭证 取现金科目的凭证, 非现金科目的凭证明细
        LambdaQueryWrapper<VoucherEntity> qw = new LambdaQueryWrapper<>();
        qw.ge(CollectionUtil.isNotEmpty(vo.getAccountingperiods()), VoucherEntity::getVoucherdate, start);
        qw.le(CollectionUtil.isNotEmpty(vo.getAccountingperiods()), VoucherEntity::getVoucherdate, end);
        qw.notIn(VoucherEntity::getIdaccount, ids);
        qw.inSql(VoucherEntity::getCode, "select DISTINCT code from qddl_voucher where idaccount in ( " +
            insql +
            ") ");
        qw.orderByDesc(VoucherEntity::getVoucherdate);
        List<VoucherEntity> vouchers = voucherService.list(qw);
        if (CollectionUtil.isEmpty(vouchers)) {
            return Collections.EMPTY_MAP;
        }
        // 查询凭证的现金科目
        LambdaQueryWrapper<VoucherEntity> qw1 = new LambdaQueryWrapper<>();
        qw1.ge(CollectionUtil.isNotEmpty(vo.getAccountingperiods()), VoucherEntity::getVoucherdate,
            vo.getAccountingperiods().get(0));
        qw1.le(CollectionUtil.isNotEmpty(vo.getAccountingperiods()), VoucherEntity::getVoucherdate,
            vo.getAccountingperiods().get(1));
        qw1.in(VoucherEntity::getIdaccount, ids);
        qw1.inSql(VoucherEntity::getCode, "select DISTINCT code from qddl_voucher where idaccount in ( " +
            insql +
            ") ");
        List<VoucherEntity> vouchers1 = voucherService.list(qw1);

        // 分组凭证
        Map<String, List<VoucherEntity>> map1 =
            vouchers1.stream().collect(Collectors.groupingBy(VoucherEntity::getCode));

        // 按部门+科目汇总
        Map<String, List<VoucherEntity>> map =
            vouchers.stream().collect(Collectors.groupingBy(v -> v.getIdauxaccdepartment() + "_" + v.getIdaccount()));

        // 查询部门
        List<TmDepartmentDicEntity> departmentDicEntities =
            tmDepartmentDicService.list(Wrappers.<TmDepartmentDicEntity>lambdaQuery().orderByAsc(TmDepartmentDicEntity::getCode));
        Map<Integer, TmDepartmentDicEntity> depMap =
            departmentDicEntities.stream().collect(Collectors.toMap(TmDepartmentDicEntity::getId,
                Function.identity()));
        // 查询科目
        List<TmAccountDicEntity> accountDicEntities = tmAccountDicService.list();
        Map<Integer, TmAccountDicEntity> accMap = accountDicEntities.stream().collect(Collectors.toMap(TmAccountDicEntity::getId,
            Function.identity()));

        List<CashCounterpartyAccount> accounts = new ArrayList<>();
        List<XmsrhzVO> vos = new ArrayList<>();
        for (Map.Entry<String, List<VoucherEntity>> entry : map.entrySet()) {
            VoucherEntity v = entry.getValue().get(0);
            // 部门
            TmDepartmentDicEntity department = depMap.get(v.getIdauxaccdepartment());

            XmsrhzVO voTmp = XmsrhzVO.builder()
                .idaccountParent(v.getIdaccount())
                .idaccountParentCode(accMap.get(v.getIdaccount()).getCode())
                .idaccountParentName(accMap.get(v.getIdaccount()).getName())
                .idaccount(v.getIdaccount())
                .idaccountCode(accMap.get(v.getIdaccount()).getCode())
                .idaccountName(accMap.get(v.getIdaccount()).getName())
                .departCode(department.getCode())
                .departName(department.getName())
                .departParentCode(department.getCode())
                .departParentName(department.getName())
                .origamountcr(entry.getValue().stream().map(VoucherEntity::getOrigamountdr).filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                .origamountdr(entry.getValue().stream().map(VoucherEntity::getOrigamountcr).filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add))
                .build();
            // 一级部门
            if (ObjectUtil.isNotNull(department.getParentcode())) {
                voTmp.setDepartParentCode(department.getParentcode());
                voTmp.setDepartParentName(department.getParentname());
            }
            // 一级科目
            if (ObjectUtil.isNotNull(accMap.get(v.getIdaccount()).getIdparent())) {
                voTmp.setIdaccountParent(accMap.get(accMap.get(v.getIdaccount()).getIdparent()).getIdparent());
                voTmp.setIdaccountParentCode(accMap.get(accMap.get(v.getIdaccount()).getIdparent()).getCode());
                voTmp.setIdaccountParentName(accMap.get(accMap.get(v.getIdaccount()).getIdparent()).getName());
            }
            vos.add(voTmp);
        }
        vos.sort(Comparator.comparing(XmsrhzVO::getDepartCode));
        return new HashMap<String, Object>() {{
            put("list",
                vos.stream().skip((long) (vo.getPage() - 1) * vo.getSize()).limit(vo.getSize()).collect(Collectors.toList()));
            put("total", vos.size());
        }};
    }

    @Override
    public void jurMx(XmsrmxReqVO vo) {
        Map<String, Object> objectMap = getXmsrmxList(vo);
        List<XmsrmxVO> vos = (List<XmsrmxVO>) objectMap.get("list");
        List<CashCounterpartyAccount> accounts = new ArrayList<>();
        if (CollectionUtil.isEmpty(vos)) {
            return;
        }
        for (XmsrmxVO xmsrmxVO : vos) {
            CashCounterpartyAccount account = new CashCounterpartyAccount();
            BeanUtil.copyProperties(xmsrmxVO, account);
        }
        cashCounterpartyAccountService.saveBatch(accounts);
    }

}
